package pong.devices;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import pong.game.data.GameState;

import android.app.Activity;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.text.format.Formatter;
import android.util.Log;

public class WiFiComunicator {

	private static final String TAG = "WiFiComunicator";

	public static final int SERVER_MODE = 0;

	public static final int CLIENT_MODE = 1;

	public static final int OBSERVER_MODE = 2;

	private static int serverCientPort = 12345;

	private static int serverObserverPort = 23456;

	private int mode = SERVER_MODE;

	Activity activity = null;

	private float handler2X = (float) 0.5;

	private float handler1X = (float) 0.5;

	private float washerX = (float) 0.5;

	private float washerY = (float) 0.5;

	private String title = "GAME";

	NetworkEvents listener = null;

	private String pin = "1";
	/**
	 * list contains game states of current application
	 */
	private List<GameState> localStates = Collections
			.synchronizedList(new LinkedList<GameState>());

	/**
	 * list contains game states of remote application
	 */
	private List<GameState> remoteStates = Collections
			.synchronizedList(new LinkedList<GameState>());

	public WiFiComunicator(Activity activity, NetworkEvents listener) {
		this.activity = activity;
		this.listener = listener;
		String ip = getIP();
		Log.i(TAG, ip);

	}

	public String getIP() {
		WifiManager wim = (WifiManager) activity
				.getSystemService(activity.WIFI_SERVICE);

		List<WifiConfiguration> l = wim.getConfiguredNetworks();
		if (l.size() > 0) {
			WifiConfiguration wc = l.get(0);

			return Formatter.formatIpAddress(wim.getConnectionInfo()
					.getIpAddress());
		}
		return "127.0.0.1";
	}

	public void setServerMode() {
		mode = SERVER_MODE;
	}

	public void setClinetMode() {
		mode = CLIENT_MODE;
	}

	public void setObserverMode() {
		mode = OBSERVER_MODE;
	}

	public void runComunication() {
		if (mode == SERVER_MODE) {
			runServer();
		} else if (mode == CLIENT_MODE) {
			runClient();
		} else if (mode == OBSERVER_MODE) {
			runObserver();
		}
	}

	public void stopComunication() {

	}

	public float getHandler2X() {
		return (float) handler2X;
	}

	public float getHandler1X() {
		return (float) handler1X;
	}

	public float getWasherX() {
		return (float) washerX;
	}

	public float getWasherY() {
		return (float) washerY;
	}

	public void setHandler2X(float v) {
		handler2X = v;
	}

	public void setHandler1X(float v) {
		handler1X = v;
	}

	public void setWasherX(float v) {
		washerX = v;
	}

	public void setWasherY(float v) {
		washerY = v;
	}

	private void runServer() {
		Server server = new Server();
		server.start();
		ServerObserver serverObserver = new ServerObserver();
		serverObserver.start();

	}

	private void runClient() {
		Client client = new Client();
		client.start();
	}

	private void runObserver() {
		ClientObserver client = new ClientObserver();
		client.start();
	}

	public String getPin() {
		return pin;
	}

	public void setPin(String pin) {
		this.pin = pin;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	private class Server extends Thread {
		Boolean end = false;

		public void stopServer() {
			end = true;
		}

		public void run() {

			try {

				ServerSocket ss = new ServerSocket(serverCientPort);
				while (!end) {
					// Server is waiting for client here, if needed
					Log.d(TAG, "Accept: ");
					Socket s = ss.accept();

					listener.onClientConnected();
					// BufferedReader input = new BufferedReader(new
					// InputStreamReader(s.getInputStream()));
					DataInputStream dataInputStream = new DataInputStream(
							s.getInputStream());
					// PrintWriter output = new
					// PrintWriter(s.getOutputStream(),true); //Autoflush
					DataOutputStream dataOutputStream = new DataOutputStream(
							s.getOutputStream());
					// String st = input.readLine();

					// long msgId = 0;

					while (!end) {
						JSONObject json = new JSONObject();
						try {
							// json.put("id", msgId);
							json.put("handler1X", handler1X);
							// json.put("handler2X", handler2X);
							json.put("washerX", washerX);
							json.put("washerY", washerY);
							json.put("title", title);
							dataOutputStream.writeUTF(json.toString());
							Log.i(TAG, "Server to Client " + json.toString());

							String str = dataInputStream.readUTF();
							Log.i(TAG, "Client to Server " + str);
							try {
								// Log.i(TAG, "Client: " + str);
								JSONObject json2 = new JSONObject(str);

								if (json2.has("handler2X")) {
									handler2X = (float) json2
											.getDouble("handler2X");
								}
							} catch (JSONException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}

							try {
								Thread.sleep(1000 * 1 / 20);
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						} catch (JSONException e) {
							e.printStackTrace();
						}

					}

					s.close();

				}
				ss.close();

			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			listener.onClientDisConnected();

		}
	}

	private class Client extends Thread {

		Boolean end = false;

		public void stopServer() {
			end = true;
		}

		public void run() {
			Socket socket = null;
			DataOutputStream dataOutputStream = null;
			DataInputStream dataInputStream = null;

			try {
				String ip = getIP();
				String newIP = ip.substring(0, ip.lastIndexOf(".") + 1) + pin;

				Log.w(TAG, ip + "  ->  " + newIP);

				socket = new Socket(newIP, serverCientPort);
				dataOutputStream = new DataOutputStream(
						socket.getOutputStream());
				dataInputStream = new DataInputStream(socket.getInputStream());

				while (!end) {

					String str = dataInputStream.readUTF();
					try {
						Log.i(TAG, "Server to Client" + str);
						JSONObject json = new JSONObject(str);
						if (json.has("handler1X")) {
							handler1X = (float) json.getDouble("handler1X");
						}
						/*
						 * if (json.has("handler2X")) { handler2X = (float)
						 * json.getDouble("handler2X"); }
						 */

						if (json.has("washerX")) {
							washerX = (float) json.getDouble("washerX");
						}
						if (json.has("washerY")) {
							washerY = (float) json.getDouble("washerY");
						}
						if (json.has("title")) {
							title = json.getString("title");
						}
					} catch (JSONException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					JSONObject json = new JSONObject();
					try {

						json.put("handler2X", handler2X);

						dataOutputStream.writeUTF(json.toString());
						Log.i(TAG, "Cient to Server: " + json.toString());

					} catch (JSONException e) {
						e.printStackTrace();
					}
				}

			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if (socket != null) {
					try {
						socket.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

				if (dataOutputStream != null) {
					try {
						dataOutputStream.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

				if (dataInputStream != null) {
					try {
						dataInputStream.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}

			listener.onClientDisConnected();
		}
	}

	private class ServerObserver extends Thread {
		Boolean end = false;

		private final static String TAG = "ServerObserver";

		public void stopServer() {
			end = true;
		}

		public void run() {

			try {

				ServerSocket ss = new ServerSocket(serverObserverPort);
				while (!end) {
					// Server is waiting for client here, if needed
					Log.d(TAG, "Observer Accept: " + serverObserverPort);
					Socket s = ss.accept();
					Log.d(TAG, "Observer Accepted: " + serverObserverPort);

					// BufferedReader input = new BufferedReader(new
					// InputStreamReader(s.getInputStream()));
					DataInputStream dataInputStream = new DataInputStream(
							s.getInputStream());
					// PrintWriter output = new
					// PrintWriter(s.getOutputStream(),true); //Autoflush
					DataOutputStream dataOutputStream = new DataOutputStream(
							s.getOutputStream());
					// String st = input.readLine();

					while (!end) {
						JSONObject json = new JSONObject();
						try {

							json.put("handler1X", handler1X);
							json.put("handler2X", handler2X);
							json.put("washerX", washerX);
							json.put("washerY", washerY);
							json.put("title", title);
							dataOutputStream.writeUTF(json.toString());
							Log.i(TAG, "Server observer: " + json.toString());

						} catch (JSONException e) {
							e.printStackTrace();
						}

						try {
							Thread.sleep(1000 * 1 / 20);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}

					}

					s.close();

				}
				ss.close();

			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}

	private class ClientObserver extends Thread {

		private final static String TAG = "ClientObserver";

		Boolean end = false;

		public void stopServer() {
			end = true;
		}

		public void run() {
			Socket socket = null;
			DataOutputStream dataOutputStream = null;
			DataInputStream dataInputStream = null;

			try {
				String ip = getIP();
				String newIP = ip.substring(0, ip.lastIndexOf(".") + 1) + pin;

				Log.w(TAG, ip + "  ->  " + newIP);

				socket = new Socket(newIP, serverObserverPort);
				Log.w(TAG, "connectd");
				dataOutputStream = new DataOutputStream(
						socket.getOutputStream());
				dataInputStream = new DataInputStream(socket.getInputStream());

				while (!end) {

					String str = dataInputStream.readUTF();
					try {
						Log.i(TAG, "Client: " + str);
						JSONObject json = new JSONObject(str);
						if (json.has("handler1X")) {
							handler1X = (float) json.getDouble("handler1X");
						}

						if (json.has("handler2X")) {
							handler2X = (float) json.getDouble("handler2X");
						}

						if (json.has("washerX")) {
							washerX = (float) json.getDouble("washerX");
						}
						if (json.has("washerY")) {
							washerY = (float) json.getDouble("washerY");
						}
						if (json.has("title")) {
							title = json.getString("title");
						}
					} catch (JSONException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

				}

			} catch (UnknownHostException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				if (socket != null) {
					try {
						socket.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

				if (dataOutputStream != null) {
					try {
						dataOutputStream.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

				if (dataInputStream != null) {
					try {
						dataInputStream.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}

			}

			listener.onClientDisConnected();
		}
	}

	public void pushGameState(GameState state) {
		localStates.add(state);
	}

	public GameState popGameState() {
		if (!remoteStates.isEmpty()) {
			return remoteStates.remove(0);
		}
		return null;
	}

	public boolean hasNewState() {
		// !instates.isEmpty
		return false;
	}

}
